﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using CoolCode.Data.Linq;

namespace CoolCode.Search
{
	/// <summary>
	/// 基于Linq+分词的搜索引擎
	/// </summary>
	public class LinqSearchEngine : ILinqSearchEngine
	{
		private SegmentEngine seg = null;
		private IHighlighter highlighter = null;
		public IHighlighter Highlighter
		{
			get
			{
				if (highlighter == null)
					highlighter = new ProHighlighter(this.seg);
				return highlighter;
			}
		}

		public SegmentEngine Parser
		{
			get { return this.seg; }
		}

		/// <summary>
		/// 是否限定分词数量
		/// </summary>
		public int MaxWordsCount { get; set; }

		public LinqSearchEngine()
			: this(null)
		{
		}

		public LinqSearchEngine(SegmentEngine seg)
		{
			this.seg = seg;
			MaxWordsCount = 10;
		}

		public SegmentResult Parse(string text)
		{
			return seg.Parse(text);
		}

		public void Highlight(string q, SearchResults results)
		{
			Highlighter.Highlight(q, results);
		}

		public IQueryable<T> Search<T>(string text, IQueryable<T> source, string[] searchFields)
		{
			if (searchFields == null)
				throw new ArgumentNullException("searchFields");
			if (searchFields.Length == 0)
				throw new NotSupportedException("searchFields.Length=0");

			IList<string> words = null;
			if (seg == null)
			{/*没有分词组件*/
				words = new List<string>();
				words.Add(text);
			}
			else
			{
				/*分词结果*/
				SegmentResult segResult = seg.Parse(text);
				words = segResult;
				if (MaxWordsCount > 0)/*是否限定分词数量*/
				{
					words = words.Take(MaxWordsCount).ToList();
				}
			}
			if (words.Count == 0)
			{/*分词结果为0， 那么直接用用户输入的值来搜索*/
				words = new List<string>();
				words.Add(text);
			}

			int wordsCount = words.Count;

			Type type_string = typeof(string);
			Type type_entity = typeof(T);

			ParameterExpression exp = Expression.Parameter(typeof(T), "s");
			//获取要查询的属性
			MemberExpression[] members = new MemberExpression[searchFields.Length];
			for (int i = 0; i < members.Length; i++)
				members[i] = Expression.Property(exp, searchFields[i]);

			//获取这些属性的Contains的方法                          
			var m = type_string.GetMethod("Contains", new Type[] { type_string });
			MethodCallExpression[,] methodCalls = new MethodCallExpression[searchFields.Length, wordsCount];

			for (int i = 0; i < members.Length; i++)
			{
				for (int j = 0; j < wordsCount; j++)
				{
					var arg = Expression.Constant(words[j], type_string);
					methodCalls[i, j] = Expression.Call(members[i], m, new Expression[] { arg });
					//methodCalls[i, j] = Expression.Call(members[i],
					//     "Contains", new Type[] { type_string }, new Expression[] { arg });
					//Expression.Call(type_string, "Contains", new Type[] { type_string }, new Expression[] { Expression.Constant(words[j], type_string) });

				}
			}
			//拼凑条件
			Expression cond = Expression.Constant(true);
			for (int j = 0; j < wordsCount; j++)
			{
				Expression subcond = Expression.Constant(false);
				for (int i = 0; i < members.Length; i++)
				{
					subcond = BinaryExpression.Or(subcond, methodCalls[i, j]);
				}
				cond = BinaryExpression.And(cond, subcond);
			}
			// lambda
			var lambda = Expression.Lambda<Func<T, bool>>(cond, new ParameterExpression[] { exp });
			return source.Where(lambda);

			//     return source.Provider.CreateQuery<T>(cond);
			// return (IQueryable<T>)Search(text, (IQueryable)source, searchFields);
		}

		public IQueryable Search(string text, IQueryable source, string[] searchFields)
		{
			if (searchFields == null)
				throw new ArgumentNullException("searchFields");
			if (searchFields.Length == 0)
				throw new NotSupportedException("searchFields.Length=0");

			SegmentResult segResult = new SegmentResult();
			segResult.Add(text);

			Type type_string = typeof(string);
			Type type_entity = source.Expression.Type.GetElementTypeOrSelf();
			ParameterExpression exp = Expression.Parameter(type_entity, "s");

			IList<string> words = segResult;
			if (MaxWordsCount > 0)/*是否限定分词数量*/
			{
				words = words.Take(MaxWordsCount).ToList();
			}
			int wordsCount = words.Count;

			//获取要查询的属性
			MemberExpression[] members = new MemberExpression[searchFields.Length];
			for (int i = 0; i < members.Length; i++)
				members[i] = Expression.Property(exp, searchFields[i]);

			//获取这些属性的Contains的方法                          
			var m = type_string.GetMethod("Contains", new Type[] { type_string });
			MethodCallExpression[,] methodCalls = new MethodCallExpression[searchFields.Length, wordsCount];

			for (int i = 0; i < members.Length; i++)
			{
				for (int j = 0; j < wordsCount; j++)
				{
					var arg = Expression.Constant(words[j], type_string);
					methodCalls[i, j] = Expression.Call(members[i], m, new Expression[] { arg });
					//methodCalls[i, j] = Expression.Call(members[i],
					//     "Contains", new Type[] { type_string }, new Expression[] { arg });
					//Expression.Call(type_string, "Contains", new Type[] { type_string }, new Expression[] { Expression.Constant(words[j], type_string) });

				}
			}
			//拼凑条件
			Expression cond = Expression.Constant(true);
			for (int j = 0; j < wordsCount; j++)
			{
				Expression subcond = Expression.Constant(false);
				for (int i = 0; i < members.Length; i++)
				{
					subcond = BinaryExpression.Or(subcond, methodCalls[i, j]);
				}
				cond = BinaryExpression.And(cond, subcond);
			}
			// lambda
			var lambda = Expression.Lambda(cond, new ParameterExpression[] { exp });
			return source.Provider.CreateQuery(cond);
		}


	}
}
